package com.bq.server.base;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.conditions.query.ChainQuery;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * service 基础类，建议各个系统定义自己的service基础类继承该类
 * <p/>
 * 它是基于mybatis plus {@link ServiceImpl} 实现，如果系统不是mybatis plus不要继承该service基础类
 *
 * @author: ChenSH
 * @date: Create by in 下午4:29 2018/5/2
 */
@Slf4j
public abstract class BaseServiceImpl<MAPPER extends BaseMapper<TEntity>, TEntity>
        extends ServiceImpl<MAPPER, TEntity> {

    @Override
    public boolean save(TEntity entity) {
        entityNotNull(entity);
        beforeSave(entity);
        boolean b = super.save(entity);
        afterSave(entity);
        return b;
    }

    /**
     * 保存前
     */
    protected void beforeSave(TEntity entity) {
        if (entity instanceof CreatedEntity) {
            CreatedEntity<?> createdEntity = (CreatedEntity<?>) entity;
            String nickname = getNickname();
            if (StrUtil.isBlank(createdEntity.getCreatedBy()) && StrUtil.isNotBlank(nickname)) {
                createdEntity.setCreatedBy(nickname);
            }
            if (Objects.isNull(createdEntity.getCreatedDate())) {
                createdEntity.setCreatedDate(LocalDateTime.now());
            }

        }

    }


    /**
     * 保存后
     */
    protected void afterSave(TEntity entity) {
        log.info("保存数据成功,{}", entity);
    }

    @Override
    public boolean saveBatch(Collection<TEntity> entityList, int batchSize) {
        entityList.forEach(this::beforeSave);
        boolean result = super.saveBatch(entityList, batchSize);
        entityList.forEach(this::afterSave);
        return result;
    }


    protected void entityNotNull(TEntity entity) {
        Assert.notNull(entity, " entityNotNull must not be null");
    }

    protected boolean isExists(TEntity entity) {
        return this.getBaseMapper().isExists(entity);
    }

    /**
     * 根据主键更新,实体中必须包含 id字段或setId方法
     *
     * @param id     主键
     * @param entity 更新实体
     */
    protected boolean updateById(Long id, TEntity entity) {
        if (entity instanceof IdEntity) {
            ((IdEntity<?>) entity).setId(id);
        } else {
            BeanUtil.setProperty(entity, "id", id);
        }
        beforeUpdate(entity);

        return this.updateById(entity);
    }

    protected void beforeUpdate(TEntity entity) {
        //更新字段处理
        if (entity instanceof UpdatedEntity) {
            UpdatedEntity<?> updatedEntity = (UpdatedEntity<?>) entity;

            String nickname = getNickname();
            if (StrUtil.isBlank(updatedEntity.getUpdatedBy()) && StrUtil.isNotBlank(nickname)) {
                updatedEntity.setUpdatedBy(nickname);
            }
            if (Objects.isNull(updatedEntity.getUpdatedDate())) {
                updatedEntity.setUpdatedDate(LocalDateTime.now());
            }
        }
    }

    /**
     * 当前登录人昵称
     *
     * @return 昵称，null 代表没有登录人
     */
    protected abstract String getNickname();

    /**
     * 分页处理，对spring 分页的封装
     *
     * @param <T>      查询mapper返回类型
     * @param pageable 分页参数
     * @param iSelect  回调查询接口
     */
    public <T> Page<T> findPage(Pageable pageable,
                                Function<IPage<T>, List<T>> iSelect) {

        IPage<T> page = pageConversion(pageable);
        List<T> list = iSelect.apply(page);
        return new JacksonPage<>(list, pageable, page.getTotal());
    }

    /**
     * 分页处理，对spring 分页的封装,只针对单表查询
     *
     * @param pageable     分页参数
     * @param queryWrapper 查询参数
     */
    public Page<TEntity> findPage(Pageable pageable, ChainQuery<TEntity> queryWrapper) {
        IPage<TEntity> page = queryWrapper.page(pageConversion(pageable));
        return new JacksonPage<>(page.getRecords(), pageable, page.getTotal());
    }

    /**
     * 分页转换 {@link Pageable} 转换成 {@link IPage}
     *
     * @param pageable spring mvc Pageable
     * @param <T>      分页数据类型
     * @return 分页结果
     */
    protected <T> IPage<T> pageConversion(Pageable pageable) {
        int pageNum = pageable.getPageNumber() + 1;
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<T> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageable.getPageSize());
        page.setOptimizeCountSql(true);
        Sort sort = pageable.getSort();
        List<OrderItem> orderItemList = sort.get()
                .map(order -> {
                    Sort.Direction direction = order.getDirection();
                    if (direction == Sort.Direction.ASC) {
                        return OrderItem.asc(order.getProperty());
                    } else {
                        return OrderItem.desc(order.getProperty());
                    }
                })
                .collect(Collectors.toList());
        page.addOrder(orderItemList);
        return page;
    }


}
